package io.github.luons.mediator.core.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * jacksonUtils
 *
 * @author : luons
 */
@Slf4j
public class JacksonUtils {

    /**
     * common mapper
     */
    private static final ObjectMapper MAPPER = newMapper();

    private static final MapType TYPE_MAP = MAPPER.getTypeFactory().constructMapType(HashMap.class, Object.class, Object.class);

    /**
     * create new ObjectMapper
     */
    public static ObjectMapper newMapper() {
        return new ObjectMapper()
                .registerModule(new JavaTimeModule())
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    }

    public static boolean isJson(String jsonString) {
        try {
            boolean isArray = jsonString.matches("\\[(.*?)\\]");
            if (!isArray) {
                return Objects.nonNull(parse(jsonString, Map.class));
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * T to json
     */
    public static <T> String toJson(T obj) {
        if (obj == null) {
            return "{}";
        } else if (obj instanceof String) {
            return (String) obj;
        }
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("JacksonUtils.toJson is exception " + e);
        }
        return obj.toString();
    }

    /**
     * T to Json by mapper
     */
    public static <T> String toJson(T obj, boolean mapper) {
        try {
            if (mapper) {
                ObjectMapper mapper1 = new ObjectMapper()
                        .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
                return mapper1.writeValueAsString(obj);
            }
            return MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("JacksonUtils.toJson is exception " + e);
        }
        return "";
    }

    /**
     * T to Bytes
     */
    public static <T> byte[] toByte(T obj) {
        if (obj == null) {
            return new byte[0];
        } else if (obj instanceof String) {
            return ((String) obj).getBytes();
        }
        try {
            return MAPPER.writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            log.error("JacksonUtils.toJson is exception " + e);
        }
        return obj.toString().getBytes();
    }

    /**
     * Object to List Entity
     */
    public static <T> List<T> parseAsList(Object obj, Class<T> itemType) {
        if (obj == null) {
            return null;
        } else if (obj instanceof String) {
            return parseAsList(obj.toString(), itemType);
        }
        return parseAsList(toJson(obj), itemType);
    }

    /**
     * json 2 List<T>
     */
    public static <T> List<T> parseAsList(String json, Class<T> itemType) {
        return parse(json, MAPPER.getTypeFactory().constructCollectionType(List.class, itemType));
    }

    /**
     * Map 2 T
     */
    public static <T> T parse(Map map, Class<T> type) {
        if (Objects.isNull(map)) {
            return null;
        }
        try {
            return MAPPER.readValue(toJson(map), type);
        } catch (IOException e) {
            log.error("JacksonUtils.parse is exception " + e);
        }
        return null;
    }

    /**
     * json 2 entity
     */
    public static <T> T parse(String json, Class<T> type) {
        try {
            return MAPPER.readValue(json, type);
        } catch (IOException e) {
            log.error("JacksonUtils.parse is exception " + e);
        }
        return null;
    }

    /**
     * json 2 entity
     */
    public static <T> T parse(String json, JavaType type) {
        try {
            return MAPPER.readValue(json, type);
        } catch (IOException e) {
            log.error("JacksonUtils.parse{} is exception " + e, json);
        }
        return null;
    }

    public static Map parseAsMap(String json) {
        return parse(json, TYPE_MAP);
    }

    public static Map<String, Object> parseAsMapKeyString(String json) {
        return parse(json, TYPE_MAP);
    }

    public static Map<String, String> parseAsMapString(String json) {
        return parse(json, TYPE_MAP);
    }

    public static Map<String, Object> bean2Map(Object obj) {
        if (obj == null) {
            return null;
        }
        return parse(toJson(obj), TYPE_MAP);
    }

}
